/**
 * dgraph.c
 *
 *  Created on: Feb 11, 2011
 *      Author: Marco Luisi
 */

#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#include "dgraph.h"
#include "error_checks.h"

void free_edge_list (edge_t *);
edge_t* copy_edge_list (edge_t *);

edge_t* newEdge();


/** crea un grafo
 \param n numero dei nodi del grafo
 \param lbl array di etichette de nodi formato:
 static const char* citta[] = {
 "PISA",
 "LUCCA",
 "SIENA",
 "LIVORNO",
 NULL,
 };

 Attenzione: le citta possono contenere solo caratteri alfanumerici e lo spazio ' '

 \retval p puntatore al nuovo grafo (se tutto e' andato bene)
 \retval NULL se si e' verificato un errore (setta errno)
 (errno == EINTR se i parametri non sono validi es lbl a NULL)
 (errno == ENOMEM se l'allocazione e' fallita)
 */

graph_t* new_graph(unsigned int n, char **lbl) {
	//controllare che concordino i numeri di size
	graph_t *g;
	int i;
	int check;

	if (lbl == NULL) {
		errno = EINTR;
		return NULL;
	}

	if (n == 0) {
		errno = EINTR;
		return NULL;
	}

	if (*lbl == NULL){
		errno = EINTR;
		return NULL;
	}

	MALLOC(g, 1, graph_t);
	if (g == NULL) {
		errno = ENOMEM;
		return NULL;
	}
	g->size = n;
	/*Riempimento nodi*/
	MALLOC(g->node, n, node_t);

	for (i = 0; i < n; i++) {
		/*Controllo stringa*/
		/*forse inutile fare le free in caso di errore?!*/

		for (check = 0; check < strlen(lbl[i]); check++) {//da fare controllo sulla LUNGHEZZA
			//fa schifo, migliorare
			if (isalnum(lbl[i][check]) == 0 ) { /*se trova carattere non conforme */ //rivedere isspace
				if (lbl[i][check] != ' '){
					errno = EINTR;
					return NULL;
				}
			}
			/*Stringa conforme, si copia*/
		}

		g->node[i].label = malloc( sizeof(char) * LLABEL);
		strncpy(g->node[i].label, lbl[i], LLABEL);
      	if (g->node[i].label == NULL) { /*errno già settato da strndup*/
			free(g);
			return NULL;
		}
		g->node[i].adj = NULL;
	} //fine ciclo copia nodi
	return g;
}



void free_graph (graph_t** g){
	unsigned int i;
	unsigned int sz;
	graph_t *gr;

	if (g == NULL){
		return;
	}

	if (*g == NULL){ //conditional jump or move on unitialised value
		return;
	}
	gr = *g;
	sz = (*g)->size;
	for(i = 0; i < sz; i++){
		free((*g)->node[i].label);
		(*g)->node[i].label = NULL;
		/*Si libera la lista di adiacenza*/
		free_edge_list((*g)->node[i].adj);
	}

	free((*g)->node);
	(*g)->node = NULL;
	free(*g);
	*g = NULL;
	return;
}

void free_edge_list(edge_t *e){

	edge_t *iter;
	while ( e != NULL){
		iter = e->next;
		//printf("e %d\n",e->label);
		free(e);
		e = iter;
	}
	e = NULL;


	return;
}


//FARE UNA PRINT NODE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
void print_graph (graph_t* g){
	unsigned int i;
	edge_t *e;
	if(g == NULL){
		printf("Errore grafo NULL\n");
		return;
	}
	printf("Stampa del grafo di %d nodi:\n",g->size);
	for (i = 0; i < g->size; i++ ){
		if(g->node[i].adj == NULL){
			printf("Nodo %s senza connessioni in uscita\n",g->node[i].label);
		}
		else {
			printf("Destinazioni raggiungibili da %s codice %d:\n", g->node[i].label,i);
			e = g->node[i].adj;
			while(e!= NULL){
				printf("%s --> %s, distanza %5.1f\n",g->node[i].label,g->node[e->label].label, e->km);
				e = e->next;
			}
		}
	}
	printf("Fine stampa.\n");
	return;
}


/** crea una copia completa del grafo (allocando tutta la memoria necessaria)
    \param g puntatore al grafo da copiare

    \retval p puntatore al nuovo grafo (se tutto e' andato bene)
    \retval NULL se si \e verificato un errore -- setta errno
    errno == ENOMEM se l'allocazione e' fallita
    errno == EINVAL se i parametri sono inconsistenti (esempio, g NULL) */
graph_t* copy_graph (graph_t* g){
	unsigned int sz;
	graph_t* ng;
	int i;

	if (g == NULL) {
		errno = EINVAL;
		return NULL;
	}
	sz = g->size;

	MALLOC(ng,1,graph_t);
	MALLOC(ng->node,sz, node_t);
	ng->size = sz;
	/*Ciclo di copia nodi*/
	for(i = 0; i < sz; i++){
		ng->node[i].label = strndup(g->node[i].label,LLABEL);
		if(ng->node[i].label == NULL){
			errno = ENOMEM;
			return NULL;
		}
		/*Copia della lista adiacenza del nodo*/
		ng->node[i].adj = copy_edge_list(g->node[i].adj);
	}

	return ng;
}

/**
 *
 * @param
 * @return
 */
edge_t* copy_edge_list(edge_t* e){
edge_t* c;
	if(e != NULL) {
		MALLOC(c,1,edge_t);
		c->km = e->km;
		c->label = e->label;
		c->next = copy_edge_list(e->next);
		return c;
	}

	return NULL;

}


/** aggiunge un arco al grafo (senza ripetizioni, ogni arco deve connettere una diversa coppia sorgente-destinazione)
    \param g puntatore al grafo
    \param e arco, stringa di formato
       LBLSORGENTE:LBLDESTINAZIONE:DISTANZA_IN_KM
       Esempio: FIRENZE:PRATO:20.4

    Attenzione: le citta possono contenere solo caratteri alfanumerici e lo spazio ' ', la distanza e' un numero reale,


    \retval 0 se l'inserzione e' avvenuta con successo
    \retval -1 se si e' verificato un errore (setta errno),
            in questo caso il grafo originario non deve essere modificato
            errno == ENOMEM se l'allocazione e' fallita
	    errno == EINVAL se l'arco e' inconsistente (esempio, il nodo sorgente/destinazione non esiste) */
int add_edge (graph_t * g, char* e){
	unsigned int from_idx;
	unsigned int to_idx;
	int chk;
	char from[LLABEL+1];
	char to[LLABEL+1];
	char dst_string[LKM+1];
	double dst = 0;
	edge_t *iter = NULL;

	//fare un fnmatch?
	/*controllo dei parametri*/
	if (g == NULL){
		errno = EINVAL;
		return(-1);
	}

	if (e == NULL){
		errno = EINVAL;
		return(-1);
	}
	chk = sscanf(e,"%[^:]:%[^:]:%[^:]",from,to,dst_string);//mettere i limiti? e i controlli

	if (chk != 3){ //necessario?
		errno = EINVAL;
		return(-1);
	}
	/*Si controlla se sono presenti i nodi*/
	from_idx = is_node(g, from);
	if(from_idx == -1){
		errno = EINVAL;
		return(-1);
	}
	to_idx = is_node(g, to);
	if(to_idx == -1 || to_idx == from_idx){
		errno = EINVAL;
		return(-1);
	}
	errno = 0;
	dst = strtod(dst_string,NULL);
	if (dst == 0){ //ok?
		printf("Formato distanza non corretto\n");
		perror("strtod: ");
		return(-1);
	}

	/*Nomi nodi e distanza legittimi*/
	/*Si controlla che non esista già un arco con stesse sorgente e destinazione*/
	iter = g->node[from_idx].adj;

	/*Nodo ancora senza connessioni*/
	if (iter == NULL){
		g->node[from_idx].adj = malloc(sizeof(edge_t));
		g->node[from_idx].adj->km = dst;
		g->node[from_idx].adj->label = to_idx;
		g->node[from_idx].adj->next = NULL;
		return(0);
	}
	//PROBABILMENTE PARTI RIDONDANTI!?!?!?!
	/*Si scorre la lista di adiacenza*/
	while(iter != NULL) { /*Si scorre la lista di adiacenza*/
		if(iter->label == to_idx){
			errno = EINVAL;
			return(-1);
		}
		if(iter->next == NULL){
			break;
		}
		iter = iter->next;
	}
	iter->next = malloc(sizeof(edge_t));
	iter->next->km = strtod(dst_string,NULL); //controllo errore..
	iter->next->label = to_idx;
	iter->next->next = NULL;
	return 0;
}

/** verifica l'esistenza di un nodo
    \param g puntatore al grafo
    \param ss label del nodo


    \retval n l'indice nel grafo (0..(n_size -1)) se il nodo esiste
    \retval -1 altrimenti
*/
int is_node(graph_t* g, char* ss){
	int i;
	if(g == NULL || ss == NULL){
		errno = EINVAL;
		return(-1);
	}
	for(i = 0; i < g->size; i++){
		if(strcmp(g->node[i].label,ss) == 0){
			return(i);
		}
	}
	return(-1);
}


/** verifica l'esistenza di un arco
    \param g puntatore al grafo
    \param n1 etichetta nodo primo estremo
    \param n2 etichetta nodo secondo estremo


    \retval TRUE se l'arco esiste
    \retval FALSE altrimenti
*/
bool_t is_edge(graph_t* g, unsigned int n1, unsigned int n2){

	edge_t *iter;

	if (g == NULL){
		return FALSE;
	}


	if(n1 > g->size || n2 > g->size){
		return FALSE;
	}

	iter = g->node[n1].adj;

	while (iter != NULL){
		if(iter->label == n2)
			return TRUE;
		else
			iter = iter->next;
	}

	return FALSE;
}


/**  grado di un nodo
    \param g puntatore al grafo
    \param lbl la label del nodo

    \retval n il numero di archi uscenti dal nodo
    \retval -1 se si e' verificato un errore (setta errno )*/
int degree(graph_t * g, char* lbl){
	//ma fare una macro ISNULL?!?!?
	int index;
	int deg;
	edge_t *tmp;

	if(g == NULL){
		errno = EINVAL;
		return -1;
	}

	if(lbl == NULL){
		errno = EINVAL;
		return -1;
	}
	index = is_node(g,lbl);

	if (index == -1){
		errno = EINVAL;
		return -1;
	}

	tmp = g->node[index].adj;
	deg = 0;
	while(tmp != NULL){
		deg++;
		tmp = tmp->next;
	}

	return deg;

}



/** numero di nodi di un grafo
    \param g puntatore al grafo

    \retval n il numero di nodi di g
    \retval -1 se si e' verificato un errore (setta errno)
*/
int n_size(graph_t* g){

	if(g == NULL){
		printf("nsize NULL\n");

		errno = EINVAL;
		return -1;
	}
	printf("nsize = %d\n",g->size);
	return g->size;
}

/** numero di archi un grafo
    \param g puntatore al grafo

    \retval n il numero di archi di g
    \retval -1 se si e' verificato un errore (setta errno)
*/
int e_size(graph_t* g){

	int num;
	unsigned int i;
	edge_t *e;


	if(g == NULL){
		errno = EINVAL;
		return -1;
	}
	num = 0;

	for (i = 0; i < g->size; i++) {
		e = g->node[i].adj;
		while(e != NULL){
			num++;
			e = e->next;
		}
	}

	return num;
}



/** carica il grafo da 2 file, il primo contiene le label possibili de nodi, ad esempio:
    PISA
    FIRENZE
    EMPOLI
    separate da '\n', il secondo contenente gli archi secondo il formato
        LBLSORGENTE:LBLDESTINAZIONE:DISTANZA_IN_KM
    Esempio: FIRENZE:PRATO:20.4
    sempre separati da '\n'

    Attenzione: le citta possono contenere solo caratteri alfanumerici e lo spazio ' ', la distanza e' un numero reale,
    ci possono essere linee vuote (cioe' che contengono solo '\n')

    \param fdnodes file descriptor del file contenente le label dei nodi
    \param fdarcs file descriptor del file contenente gli archi

    \retval g puntatore al nuovo grafo se il caricamento e' avvenuto con successo
    \retval NULL se si e' verificato un errore (setta errno), es
             errno == ENOMEM se l'allocazione e' fallita
	    errno == EINVAL se i parametri sono inconsistenti (esempio, fd non valido) */
graph_t* load_graph (FILE * fdnodes, FILE * fdarcs){

	unsigned int nodes = 0;
	unsigned int dim = 30; //macro
	int len;
	int i;
	char **labels;
	char parse_buffer[2 + LKM + LLABEL * 2];
	graph_t *g;

	if(fdnodes == NULL || fdarcs == NULL){
		errno = EINVAL;
		return NULL;
	}

	MALLOC(labels,dim,sizeof(char*));
	/*Parsing dei nodi*/
	while( fgets(parse_buffer, LLABEL, fdnodes) != NULL){ // +1?
		len = strlen(parse_buffer);
		len --;
		parse_buffer[len] = '\0';
		for(i = 0; i < len; i++){ //controllare come si comporta con le linee vuote
			if (isalnum(parse_buffer[i]) == 0 && parse_buffer[i] != ' '){
				printf("Errore, nodo %s contenente caratteri non regolari!\n", parse_buffer);
				errno = EINVAL;// !?
				return NULL; //si esce o si salta?
				}

			}

			if(nodes == dim){
				dim = dim * 2;
				labels = realloc(labels,dim * sizeof(char*));
				//controllo fallimento realloc?!
			}
			/*Nome nodo consono alle specifiche*/
			labels[nodes] = strndup(parse_buffer, LLABEL);

			nodes++;
	} //fine while fgets
	//realloc finale di labels?
	//labels = realloc(labels, nodes * sizeof(char*));
	g = new_graph(nodes, labels);//vedere bene come liberare i buffers
	for(i = 0;i < nodes;i++){
		free(labels[i]);
	}
	free(labels);
	if (g == NULL){
		return(NULL); //errno settata da newgraph
	}

	/*Parsing degli archi*/
	while( fgets(parse_buffer,3 + LKM + LLABEL * 2,fdarcs) != NULL){
		if(add_edge(g,parse_buffer) == -1){
			printf("ADDEDGE NULL\n");
			return(NULL);
		}
	}
	return(g);
}

/** salva il grafo su due file, formato \see load_graph

    \param fdnodes file descriptor del file che dovra' contenere i nodi del grafo
    \param fdarcs file descriptor del file che dovra' contenere gli archi del grafo

    \param g grafo da scrivere

    \retval 0 se la scrittura e' andata a buon fine
    \retval -1 se si e' verificato un errore (setta errno), es. errno == EIO se la scrittura e' fallita
*/
int save_graph (FILE * fdnodes, FILE * fdarcs, graph_t* g){

	unsigned int i;
	unsigned int sz;
	edge_t *e;

	//controllo null?
	if(fdnodes == NULL || fdarcs == NULL || g == NULL){
		errno = EINVAL;
		return(-1);
	}
	printf("Salvataggio in corso...\n");
	sz = g->size;

	for(i = 0; i < sz; i++){
		fprintf(fdnodes,"%s\n",g->node[i].label);
		e = g->node[i].adj;
		while(e != NULL){
			fprintf(fdarcs,"%s:%s:%3.1f\n",g->node[i].label,g->node[e->label].label, e->km);
			e = e->next;
		}
	}
	printf("Salvataggio terminato\n");

	return(0);
}



